#ifndef PROCESSMANAGER_H
#define PROCESSMANAGER_H

#include <QObject>
#include <QSharedPointer>
#include <QProcess>
#include <QMap>
#include <QMutex>

class ProcessInfo : public QObject{
    friend class ProcessManager;
private:
    ProcessInfo(QObject *parent=nullptr):QObject(parent),stateChangedBlock(nullptr),errorOccurredBlock(nullptr),readyReadStandardOutputBlock(nullptr){}
    std::function<void(QProcess::ProcessState)> stateChangedBlock;
    std::function<void(QProcess::ProcessError)> errorOccurredBlock;
    std::function<void(QByteArray)> readyReadStandardOutputBlock;
    QProcess process;
};

class ProcessManager : public QObject
{
    Q_OBJECT
public:
    ProcessManager(QObject *parent = nullptr);

    static void killApp(const QString &appName);
    static void killApp(const QStringList &appNames);
    static bool existsApp(const QString &appName);
    static ProcessManager* instance();

    bool exists(const QString &name);
    bool existsPrefix(const QString &prefix);
    QProcess::ProcessState state(const QString &name);
    void close(const QString &name);
    void closePrefix(const QString &prefix);
    void async(const QString &name, const QString &appPath, const QStringList &param, const QString &workDir = "");
    void async(const QString &name, const QString &appPath, const QString &workDir = "");
    void async(const QString &name,
               const QString &appPath,
               const QStringList &param,
               const QString &workDir,
               std::function<void(QByteArray data)> readyReadBlock,
               std::function<void(QProcess::ProcessState newState)> stateChangedBlock,
               std::function<void(QProcess::ProcessError error)> errorOccurredBlock);
    void write(const QString &name, const QByteArray &data);

signals:
    void sigStateChanged(QString name, QProcess::ProcessState state);
    void sigErrorOccurred(QString name, QProcess::ProcessError error);
    void sigReadReady(QString name, QByteArray data);

private:
    QSharedPointer<ProcessInfo> getProcess(const QString &name);
    QList<QSharedPointer<ProcessInfo>> getProcessWithPrefix(const QString &prefix);
    QSharedPointer<ProcessInfo> takeProcess(const QString &name);
    QList<QSharedPointer<ProcessInfo>> takeProcessWithPrefix(const QString &prefix);

    QString getName(QProcess *process);
    void addProcess(const QString &name, QSharedPointer<ProcessInfo> process, bool stateConnect = true, bool errorConnect = true, bool readyReadConnect = false);
    void removeProcess(const QString &name);
    void removeProcessWithPrefix(const QString &prefix);

    Q_SIGNAL void sigDeleteLater(QSharedPointer<QObject> object);
    Q_SLOT void onDeleteLater(QSharedPointer<QObject> object){Q_UNUSED(object);}

private slots:
    void process_onStateChanged(QProcess::ProcessState newState);
    void process_onErrorOccurred(QProcess::ProcessError error);
    void process_onReadyReadStandardOutput();

private:
    static ProcessManager *_instance;
    QMutex _mutex;
    QMap<QString,QSharedPointer<ProcessInfo>> _nameToProcess;
};

#endif // PROCESSMANAGER_H
